package com.cxs.controller;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.io.FileUtils;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.List;

/*
 * @Project:file-upload-senior
 * @Author:cxs
 * @Motto:放下杂念,只为迎接明天更好的自己
 * */
@WebServlet(name = "UploadController", urlPatterns = "/upload")
public class UploadController extends HttpServlet {

    private static final String UTF8 = "utf-8";
    private static final String BASEPATH = "G:\\upload_senior";

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 分片序号
        Integer chunk = null;
        // 总分片数
        Integer chunks = null;
        // 文件名
        String name = null;
        // 合并文件需要的流
        BufferedOutputStream os = null;
        try {
            request.setCharacterEncoding(UTF8);
            response.setCharacterEncoding(UTF8);
            // 创建一个文件工厂
            DiskFileItemFactory factory = new DiskFileItemFactory();
            factory.setRepository(new File(BASEPATH));
            factory.setSizeThreshold(1024);
            // 这个类可以帮我们解析request
            ServletFileUpload upload = new ServletFileUpload(factory);
            upload.setFileSizeMax(100L * 1024L * 1024L);
            upload.setSizeMax(1000L * 1024L * 1024L);
            // FileItem 包含表单字段及文件字段
            List<FileItem> fileItems = upload.parseRequest(request);
            for (FileItem fileItem : fileItems) {
                // 获取本次请求的分片、总分片、文件名
                if (fileItem.isFormField()) {
                    // 是正常的表单字段
                    if (null != fileItem.getFieldName() && "chunk".equals(fileItem.getFieldName())) {
                        // 获取到分片字段
                        chunk = Integer.parseInt(fileItem.getString(UTF8));
                    }
                    if (null != fileItem.getFieldName() && "chunks".equals(fileItem.getFieldName())) {
                        // 获取到分片总数字段
                        chunks = Integer.parseInt(fileItem.getString(UTF8));
                    }
                    if (null != fileItem.getFieldName() && "name".equals(fileItem.getFieldName())) {
                        // 获取到文件名
                        name = fileItem.getString(UTF8);
                    }
                }
            }
            // 上述循环结束，表单字段全部读取完毕
            // 假设它是整个文件，没有分片上传
            String currentFileName = name;
            for (FileItem fileItem : fileItems) {
                if (!fileItem.isFormField()) {
                    // 是文件字段
                    if (null != chunk && null != chunks) {
                        // 是分片上传，文件名起个独特的名字，方便后续合并
                        currentFileName = chunk + "_" + name;
                        // 存当前文件
                        File currentFile = new File(BASEPATH, currentFileName);
                        // 如果文件不存在，进行存储，否则假入客户端中断后重新上传会重复
                        if (!currentFile.exists()) {
                            fileItem.write(currentFile);
                        }
                    }
                }
            }
            // 是分片上传时，当上传至最后一个分片时，处理文件合并，chunk 的值 0 - chunks - 1
            if (chunk != null && chunks != null && chunk.equals(chunks - 1)) {
                // 是最后一个分片,准备合并
                File realFile = new File(BASEPATH, name);
                os = new BufferedOutputStream(new FileOutputStream(realFile));

                for (int i = 0; i < chunks; i++) {
                    // 文件名规则是我们自己定义的
                    File temp = new File(BASEPATH, i + "_" + name);
                    // 因为分片上传时并发操作，tomcat拿到请求之后会分配给一个线程去处理，我们不能保证哪个分片先到
                    // 如果不存在就一直等
                    while (!temp.exists()) {
                        // 等100ms
                        Thread.sleep(100);
                    }
                    // 说明已经到了
                    os.write(FileUtils.readFileToByteArray(temp));
                    os.flush();
                    temp.delete();
                }
                // 循环结束后再刷新一次流，防止缓冲区未满导致的部分数据缺失
                os.flush();
            }
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (FileUploadException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                // 关闭流
                if (os != null) {
                    os.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
